package client;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.StringTokenizer;

import lib.Commands;
import lib.Config;
import lib.Sound;
import lib.map.Map;
import lib.player.Player;
import lib.player.PlayerList;
import lib.scrren.Screen;
import client.gui.ingame.GamePanel;
import client.gui.lobby.Lobby;

public class TCPClientListener extends Thread {

	private BufferedReader in;
	private Commands c;
	private int PORT;
	private Socket sock;
	private PrintWriter out;
	private Sound send = new Sound("data/sounds/send.wav");
	public Deque<Client> client = new ArrayDeque<Client>();
	private Map map = new Map("");
	public Player player = new Player(0, 0, 1, 1, "null");
	public PlayerList playerList = new PlayerList(map);
	public int[] score = new int[2];

	public Lobby gui = null;

	public TCPClientListener(Client client, String host, int port)
			throws Exception {
		this.PORT = port;
		if (client != null)
			this.client.add(client);
		sock = new Socket(InetAddress.getByName(host), PORT);
		out = new PrintWriter(sock.getOutputStream(), true);
		in = new BufferedReader(new InputStreamReader(sock.getInputStream()));

	}

	public void run() {
		try {
			String line;
			while ((line = in.readLine()) != null) {
				parseCommands(line);
			}

		} catch (SocketException e) {
			System.out.println("Connection refused");
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				sock.close();
				out.close();
				in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			listenerStop();
		}
	}

	private boolean parseCommands(String s) {
		StringTokenizer st = new StringTokenizer(s, " ");
		Deque<String> args = new ArrayDeque<String>();
		while (st.hasMoreTokens()) {
			args.push(st.nextToken());
		}

		if (args.isEmpty()) {
			invalidArgumentAnswer();
			return false;
		}
		try {
			c = Commands.valueOf(args.pollLast().toUpperCase());
			if (c == null)
				return false;
			else {
				switch (c) {
				case SEND:
					display(lib.Utils.TimeStamp() + args.pollLast() + " said: "
							+ getMessage(args));
					send.play();
					break;
				case PRINT:
					display(lib.Utils.TimeStamp() + getMessage(args));
					break;
				case PING:
					send(Commands.PING, getMessage(args));
					break;
				case WHISPER:
					if (args.size() >= 2)
						display(lib.Utils.TimeStamp() + args.pollLast()
								+ " whispered to " + args.pollLast() + ": "
								+ getMessage(args));
					break;
				case KICK:
					display(lib.Utils.TimeStamp() + "User " + args.pollLast()
							+ " kicked.");
					break;
				case LOGIN:
					display("Please LOGIN first.");
					break;
				case LEAVE:
					display(lib.Utils.TimeStamp() + "User " + args.pollLast()
							+ " left.");
					break;
				case POS:
					// player: x y nr
					pos(Integer.parseInt(args.pollLast()), Integer
							.parseInt(args.pollLast()), Integer.parseInt(args
							.pollLast()));
					break;
				case JOIN:
					// [argument] xpos ypos nr team name
					if (args.peekLast().toUpperCase().equals("LOCAL")) {
						args.pollLast();
						player = createPlayer(args);
						playerList = new PlayerList(map);
						playerList.add(player);
					} else {
						Player _player = createPlayer(args);
						if (!_player.name.equals(player.name))
							playerList.add(_player);
					}
					break;
				case START:
					new GamePanel(this, map);
					Screen.getInstance().setMaster(gui);
					break;
				case ITEM:
					// playerNr item
					playerList.getPlayer(Integer.parseInt(args.pollLast())).ITEM = playerList.parser.items
							.get(args.pollLast());
					break;
				case SETMAP:// progress ?element x y
					if (args.peekLast().equals("SRC")) {
						args.pollLast();
						map.setParser(args.pollLast());
						break;
					}
					setMap(Boolean.parseBoolean(args.pollLast()), Integer
							.parseInt(args.pollLast()), Integer.parseInt(args
							.pollLast()), Integer.parseInt(args.pollLast()));
					break;
				case UNIVERSE:
					for (Client c : client) {
						c.displayInfo(args);
					}
					break;
				case POINTS:
					setPoints(Integer.parseInt(args.pollLast()), Integer
							.parseInt(args.pollLast()));
					break;
				case TEAMPOINTS:
					int team = Integer.parseInt(args.pollLast());
					int points = Integer.parseInt(args.pollLast());
					score[team - 1] = points;
					for (Player p : playerList.list) {
						if (p.Team == team)
							p.TeamPoits = points;
					}
					break;
				case LIST:
					for (Client c : client) {
						c.list(args);
					}
					break;
				default:
					return false;
				}
			}
			return true;
		} catch (IllegalArgumentException e) {
			display("Command " + s
					+ " not Supportet send HELP for more informatins.");
			e.printStackTrace();
			return false;
		}

	}

	private void pos(int x, int y, int nr) {
		try {
			Player _tmp = playerList.getPlayer(nr);
			playerList.simpleMove(_tmp, x, y);
			if (Config.debug)
				System.out.println(_tmp);
		} catch (NullPointerException e) {
			e.printStackTrace();
		}

	}

	private void setPoints(int nr, int points) {
		for (Player p : playerList.list) {
			if (p.nr == nr)
				p.points = points;
		}

	}

	int mapProgress = 0;

	private void setMap(boolean progress, int element, int i, int j) {
		map.setField(element, i, j);
		if (i + j == 0)
			mapProgress = 0;
		if (!progress)
			return;
		for (Client c : client) {
			c.progress("Loading Map", (int) (Math.sqrt(mapProgress++)));
		}
		if (mapProgress == 100 * 100)
			mapProgress = 0;

	}

	private Player createPlayer(Deque<String> args) {
		return new Player(Integer.parseInt(args.pollLast()), Integer
				.parseInt(args.pollLast()), Integer.parseInt(args.pollLast()),
				Integer.parseInt(args.pollLast()), args.pollLast());
	}

	private void display(String msg) {
		for (Client cl : client) {
			cl.displayMessage(msg);
		}
	}

	public void send(Commands cmd, String s) {
		if (client != null)
			out.println(cmd + " " + s);
	}

	public void send(String s) {
		if (client != null)
			out.println(s);
	}

	void invalidArgumentAnswer() {
		System.out.println("Command Error");
	}

	public String getMessage(Deque<String> args) {
		String message = " ";

		while (!args.isEmpty()) {
			message += args.pollLast() + " ";
		}
		return message;
	}

	public void listenerStop() {
		try {
			out.close();
			in.close();
			sock.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.exit(0);

	}

}
